import pandas as pd
#from vk_download import main
#import scipy.sparse as sprs
import time
from tqdm.auto import tqdm
import os
import random
import datetime
#from threading import Thread
#from multiprocessing import Process
import numpy as np
from scipy.sparse import csr_matrix, lil_matrix, csc_matrix
#import scipy.sparse as sprs
#from collections import OrderedDict
#import shutil
import matplotlib.pyplot as plt
#import seaborn as sns
import networkx as nx
from IPython.display import clear_output
#from statsmodels.stats.proportion import proportion_confint
#import statsmodels.api as sm
#from scipy.optimize import minimize
import matplotlib as mpl
import copy
from scipy.stats import powerlaw
import warnings
#warnings.filterwarnings('ignore')
#warnings.filterwarnings('always')








def RetrieveArguments(Arguments):
    
    N = Arguments['N']
    T = Arguments['T']
    m = Arguments['m']
    
    InitialOpinionDistribution = Arguments['InitialOpinionDistribution']
    ActivationProbabilitiesType = Arguments['ActivationProbabilitiesType']
    RankingAlgorithmType = Arguments['RankingAlgorithmType']
    Scale = Arguments['Scale']  #!!
    #N_Comments_Display = Arguments['N_Comments_Display']
    OrderOfActions = Arguments['OrderOfActions']
    
    TransitionTable = Arguments['TransitionTable']
    ReplyWritingProbabilities = Arguments['ReplyWritingProbabilities']
    LikingProbabilities = Arguments['LikingProbabilities']
    
    Basis_Like = Arguments['Basis_Like']
    Basis_Reply = Arguments['Basis_Reply']
    #ScaleFactor_Like = Arguments['ScaleFactor_Like']  #!!
    #Ratio_Like = Arguments['Ratio_Like']  #!!
    #ScaleFactor_Reply = Arguments['ScaleFactor_Reply']  #!!
    #Ratio_Reply = Arguments['Ratio_Reply']  #!!
    
    N_Bots = Arguments['N_Bots']
    T_bots = Arguments['T_bots']

    ReplyWritingProbabilities_Bots = Arguments['ReplyWritingProbabilities_Bots']
    LikingProbabilities_Bots = Arguments['LikingProbabilities_Bots']  
    
    Courtege = (N, T, m, 
                InitialOpinionDistribution, ActivationProbabilitiesType, RankingAlgorithmType, Scale, OrderOfActions, 
                TransitionTable, ReplyWritingProbabilities, LikingProbabilities, 
                Basis_Like, Basis_Reply, 
                N_Bots, T_bots, ReplyWritingProbabilities_Bots, LikingProbabilities_Bots)
    
    return Courtege 

def CreateTargetFolder(Arguments):
    
    #------------------------------------ Retrieve arguments
    
    (N, T, m, InitialOpinionDistribution, ActivationProbabilitiesType, RankingAlgorithmType, Scale, OrderOfActions, TransitionTable, ReplyWritingProbabilities, LikingProbabilities, Basis_Like, Basis_Reply, N_Bots, T_bots, ReplyWritingProbabilities_Bots, LikingProbabilities_Bots) = RetrieveArguments(Arguments)
    
    #------------------------------------ Create the target folder
    
    TargetFolder = ''
    
    TargetFolder = TargetFolder + f'{N}-'
    TargetFolder = TargetFolder + f'{T}-'
    TargetFolder = TargetFolder + f'{m}-'
    TargetFolder = TargetFolder + f'{Scale}-'
    #TargetFolder = TargetFolder + f'{N_Comments_Display}-'
    TargetFolder = TargetFolder + f'{OrderOfActions}-'
    
    for OpinionCamp in InitialOpinionDistribution:
        TargetFolder = TargetFolder + f'{OpinionCamp}-'
        
    TargetFolder = TargetFolder + ActivationProbabilitiesType
    
    for Table in TransitionTable:
        for i in range(Table.shape[0]):
            for j in range(Table.shape[0]):
                TargetFolder = TargetFolder + f'{Table[i, j]}-'
                
    for i in range(ReplyWritingProbabilities.shape[0]):
        for j in range(ReplyWritingProbabilities.shape[1]):
            TargetFolder = TargetFolder + f'{ReplyWritingProbabilities[i, j]}-'
            
    for i in range(LikingProbabilities.shape[0]):
        for j in range(LikingProbabilities.shape[1]):
            TargetFolder = TargetFolder + f'{LikingProbabilities[i, j]}-'
    
    #------------------------------------
    
    #TargetFolder = TargetFolder + f'{ScaleFactor_Like}-'
    #TargetFolder = TargetFolder + f'{Ratio_Like}-'
    #TargetFolder = TargetFolder + f'{ScaleFactor_Reply}-'
    #TargetFolder = TargetFolder + f'{Ratio_Reply}-'
    
    TargetFolder = TargetFolder + f'{Basis_Like}-'
    TargetFolder = TargetFolder + f'{Basis_Reply}-'
    
    #------------------------------------
    
    TargetFolder = TargetFolder + f'{N_Bots}-'
    TargetFolder = TargetFolder + f'{T_bots}-'
    
    for i in range(ReplyWritingProbabilities_Bots.shape[0]):
        for j in range(ReplyWritingProbabilities_Bots.shape[1]):
            TargetFolder = TargetFolder + f'{ReplyWritingProbabilities_Bots[i, j]}-'
            
    for i in range(LikingProbabilities_Bots.shape[0]):
        for j in range(LikingProbabilities_Bots.shape[1]):
            TargetFolder = TargetFolder + f'{LikingProbabilities_Bots[i, j]}-'    
    
    #------------------------------------
    
    TargetFolder = TargetFolder + f'{RankingAlgorithmType}'
    
    #------------------------------------
    
    return TargetFolder

    
def SaveExperiment(Arguments, Records):
    
    """
    This function saves the records of the experiment performed recently
    """
    
    #------------------------------------ Create space for storing experiment records
    
    directories = os.listdir()
    
    if 'Experiments_Bots' not in directories:
        os.mkdir('Experiments_Bots')
    else:
        pass
    
    directories = os.listdir(path='Experiments_Bots')
    
    #------------------------------------ Create the target folder
    
    TargetFolder = CreateTargetFolder(Arguments)
    
    #------------------------------------ Insert records with  the automatically generated ID
    
    if TargetFolder not in directories:
        os.mkdir(f'Experiments_Bots/{TargetFolder}')
    else:
        pass
    
    directories = os.listdir(path=f'Experiments_Bots/{TargetFolder}')
    
    N_Exp = 1
    
    for i in range(len(directories)):
        
        if ( directories[i][:4] == 'Exp(' ):
            
            N_Exp = N_Exp + 1
            
    os.mkdir(f'Experiments_Bots/{TargetFolder}/Exp({N_Exp})')
    
    path = f'Experiments_Bots/{TargetFolder}/Exp({N_Exp})'
    
    #------------------------------------ Save the experiment's records
    
    Records['Agents'].to_csv(f'{path}/Agents.npy', index=False)
    Records['Posts'].to_csv(f'{path}/Posts.npy', index=False)
    Records['Comments'].to_csv(f'{path}/Comments.npy', index=False)
    Records['Likes'].to_csv(f'{path}/Likes.npy', index=False)
    
    np.save(f'{path}/Activations.npy', Records['Activations'])
    np.save(f'{path}/Fractions.npy', Records['Fractions'])
    
    return 0




################################################################################################################




def LoadExperiment(Arguments, N_Exp):
    
    """
    This function loades the records of a given experiment
    """
    
    #------------------------------------ Create the target folder
    
    TargetFolder = CreateTargetFolder(Arguments)

    #------------------------------------

    path = f'Experiments_Bots/{TargetFolder}/Exp({N_Exp})'
    
    #------------------------------------ Load the experiment's records
    
    Records = {}
    
    Records['Agents'] = pd.read_csv(f'{path}/Agents.npy')
    Records['Posts'] = pd.read_csv(f'{path}/Posts.npy')
    Records['Comments'] = pd.read_csv(f'{path}/Comments.npy')
    Records['Likes'] = pd.read_csv(f'{path}/Likes.npy')
    
    Records['Activations'] = np.load(f'{path}/Activations.npy')
    Records['Fractions'] = np.load(f'{path}/Fractions.npy')
    
    return Records




def LoadExperiments(Arguments, MaxNumberOfExps=100500):
    
    """
    This function loades the records of a given experiment
    """
    
    ListOfRecords = []
    
    #------------------------------------ Create the target folder
    
    TargetFolder = CreateTargetFolder(Arguments)

    #------------------------------------ Read the target folder
    
    try: 
    
        Elements = os.listdir(path=f'Experiments_Bots/{TargetFolder}')

        Counter = 0

        for Element in Elements:

            if Element[:4] == 'Exp(':

                #------------------------------------ Load the experiment's records

                path = f'Experiments_Bots/{TargetFolder}/Exp({Element[4:-1]})'


                Records = {}

                Records['Agents'] = pd.read_csv(f'{path}/Agents.npy')
                Records['Posts'] = pd.read_csv(f'{path}/Posts.npy')
                Records['Comments'] = pd.read_csv(f'{path}/Comments.npy')
                Records['Likes'] = pd.read_csv(f'{path}/Likes.npy')

                Records['Activations'] = np.load(f'{path}/Activations.npy')
                Records['Fractions'] = np.load(f'{path}/Fractions.npy')

                #------------------------------------ Add the records to the list

                ListOfRecords.append(Records)
                
                Counter = Counter + 1
                
                if Counter > MaxNumberOfExps:
                    
                    break
                    
                else:
                    
                    pass
                
            else:
                
                pass
                
    except Exception as exception:
        
        print(exception)
        
        pass
    
    return ListOfRecords



def PrepareFixedArguments(N, T, m, OpinionAlphabet, AgentsArgs, BotsArgs):
    
    ActivationProbabilitiesType, OrderOfActions = AgentsArgs
    
    N_Bots, T_bots, ReplyWritingProbabilities_Bots, LikingProbabilities_Bots =  BotsArgs
    
    Arguments = {}
    
    Arguments['N'] = N
    Arguments['T'] = T
    Arguments['m'] = m
    Arguments['OpinionAlphabet'] = OpinionAlphabet
    
    #Arguments['InitialOpinionDistribution'] = InitialOpinionDistribution
    Arguments['ActivationProbabilitiesType'] = ActivationProbabilitiesType
    Arguments['OrderOfActions'] = OrderOfActions
    #Arguments['TransitionTable'] = TransitionTable
    
     
    Arguments['N_Bots'] = N_Bots
    Arguments['T_bots'] = T_bots
    Arguments['ReplyWritingProbabilities_Bots'] = ReplyWritingProbabilities_Bots
    Arguments['LikingProbabilities_Bots'] = LikingProbabilities_Bots
    

    
    return Arguments


def LoadSeveralExperiments(N, T, m, OpinionAlphabet, AgentsArgs, BotsArgs, VariableParameters):
    
    # Define the fixed parameters
    
    Arguments = PrepareFixedArguments(N, T, m, OpinionAlphabet, AgentsArgs, BotsArgs)
    
    # Add variable parameters
    
    RankingAlgorithmType, alpha, beta, gamma, delta, Basis_Like, Basis_Reply, Scale, omega, InitialOpinionDistribution = VariableParameters
    
    #------------------------------------------------------------------
    
    LikingProbabilities = np.array([[0.3,   0,   0], 
                                    [0,     0,   0], 
                                    [round(0 + delta, 2),     0,   round(0.3 + gamma, 2)]])

    #------------------------------------------------------------------
    
    ReplyWritingProbabilities = np.array([[0.1,            0,   0.05], 
                                          [0,              0,   0], 
                                          [round(0.05 + alpha, 2),   0,   round(0.1 + beta, 2)]])
    
    #------------------------------------------------------------------
    
    TransitionTable = [np.array([[1,      0,     0], 
                                             [1,      0,     0], 
                                             [1,      0,     0]]), 

                                   np.array([[0.1,  0.9,   0], 
                                             [0,    1,     0], 
                                             [0,    round(0.9 - omega, 2),   round(0.1 + omega, 2)]]), 

                                   np.array([[0,    0,     1], 
                                             [0,    0,     1], 
                                             [0,    0,     1]])]    
    
    
    Arguments['RankingAlgorithmType'] = RankingAlgorithmType
    
    Arguments['ReplyWritingProbabilities'] = ReplyWritingProbabilities
    Arguments['LikingProbabilities'] = LikingProbabilities    
    
    Arguments['Scale'] = Scale
    Arguments['Basis_Like'] = Basis_Like
    Arguments['Basis_Reply'] = Basis_Reply
    
    Arguments['TransitionTable'] = TransitionTable
    
    Arguments['InitialOpinionDistribution'] = InitialOpinionDistribution
    
    #------------------------------------------------------------------------
    
    # Load experiments
    
    ListOfRecords = LoadExperiments(Arguments, 100)

    ResultsFractions = []

    for Records in ListOfRecords:

        ResultsFractions.append((Records['Fractions'][0], Records['Fractions'][-1]))

    return ResultsFractions